/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//layout.hpp

#ifndef MVL_LAYOUT_HPP
#define MVL_LAYOUT_HPP

#include "type_traits.hpp"
#include "meta_vector.hpp"
#include "promote_traits.hpp"

namespace mvl {

namespace layout {

struct dense_layout_tag { };
struct sparse_layout_tag { };
struct row_major_tag { };
struct column_major_tag { };

template<int R, int C, typename Order = row_major_tag>
struct dense_layout 
{
    typedef dense_layout_tag    tag;
    typedef Order               order;
    typedef dense_layout        type;

    typedef meta::int_<R>       Rows;
    typedef meta::int_<C>       Cols;
    typedef meta::int_<R * C>   Size;
};


//value tag
struct _v_ { };
//zero tag
struct _0_ { };
//one tag
struct _1_ { };

//tag multiplication
template<typename Tag1, typename Tag2> struct mul_tags { typedef _0_ type; };
template<typename Tag> struct mul_tags<Tag, Tag> { typedef Tag type; };
template<> struct mul_tags<_1_, _1_> { typedef _1_ type; };
template<> struct mul_tags<_1_, _v_> { typedef _v_ type; };
template<> struct mul_tags<_v_, _1_> { typedef _v_ type; };
template<> struct mul_tags<_v_, _v_> { typedef _v_ type; };

//tag division
template<typename Tag1, typename Tag2> struct div_tags;
template<> struct div_tags<_0_, _1_> { typedef _0_ type; };
template<> struct div_tags<_0_, _v_> { typedef _0_ type; };
template<> struct div_tags<_1_, _1_> { typedef _1_ type; };
template<> struct div_tags<_1_, _v_> { typedef _v_ type; };
template<> struct div_tags<_v_, _1_> { typedef _v_ type; };
template<> struct div_tags<_v_, _v_> { typedef _v_ type; };

//tag addition
template<typename Tag1, typename Tag2> struct add_tags { typedef _v_ type; };
template<> struct add_tags<_0_, _0_> { typedef _0_ type; };
template<> struct add_tags<_0_, _1_> { typedef _1_ type; };
template<> struct add_tags<_1_, _0_> { typedef _1_ type; };

//tag subtraction
template<typename Tag1, typename Tag2> struct sub_tags { typedef _v_ type; };
template<> struct sub_tags<_0_, _0_> { typedef _0_ type; };
template<> struct sub_tags<_0_, _1_> { typedef _v_ type; };
template<> struct sub_tags<_1_, _0_> { typedef _1_ type; };
template<> struct sub_tags<_1_, _1_> { typedef _0_ type; };

//tag negation
template<typename Tag> struct neg_tag { typedef _v_ type; };
template<> struct neg_tag<_0_> { typedef _0_ type; };

template<int R, int C, typename Order, typename Entries, int NumVals>
struct sparse_layout 
{
    typedef sparse_layout_tag   tag;
    typedef Order               order;
    typedef Entries             entries;
    typedef sparse_layout       type;
    typedef meta::int_<R>       Rows;
    typedef meta::int_<C>       Cols;
    typedef meta::int_<NumVals> Size;
};

//generic entry type
template<typename I, typename J, typename Tag>
struct entry {
    typedef Tag     tag;           //type of entry
    typedef I       i;             //(i,j) location
    typedef J       j;
    typedef entry   type;
};

//utilities to abstract from row/column major order
template<typename I1, typename I2, typename Order> struct get1; 
template<typename I1, typename I2> struct get1<I1, I2, row_major_tag> : I1 {}; 
template<typename I1, typename I2> struct get1<I1, I2, column_major_tag> : I2 {}; 
template<typename I1, typename I2, typename Order> struct get2; 
template<typename I1, typename I2> struct get2<I1, I2, row_major_tag> : I2 {}; 
template<typename I1, typename I2> struct get2<I1, I2, column_major_tag> : I1 {}; 

//layout iterators
template<typename It1>
struct sparse_iterator1 {
    typedef sparse_iterator1 type;
};

template<typename It2>
struct sparse_iterator2 {
    typedef sparse_iterator2 type;
};

template<typename Layout, typename Pos1>
struct dense_iterator1 {
    typedef dense_iterator1 type;
};

template<typename Layout, typename Pos1, typename Pos2>
struct dense_iterator2 {
    typedef dense_iterator2 type;
};

//begin1
template<typename Tag> struct begin1_impl;

template<typename Layout>
struct begin1: begin1_impl<typename Layout::tag>::template apply<Layout> { };

template<>
struct begin1_impl<sparse_layout_tag> {
    template<typename L>
    struct apply : sparse_iterator1<typename meta::begin<typename L::entries>::type> { };
};

template<>
struct begin1_impl<dense_layout_tag> {
    template<typename L>
    struct apply : dense_iterator1<L, meta::int_<0> > { };
};

//end1
template<typename Tag> struct end1_impl;

template<typename Layout>
struct end1: end1_impl<typename Layout::tag>::template apply<Layout> { };

template<>
struct end1_impl<sparse_layout_tag> {
    template<typename L>
    struct apply : sparse_iterator1<typename meta::end<typename L::entries>::type> { };
};

template<>
struct end1_impl<dense_layout_tag> {
    template<typename L>
    struct apply : dense_iterator1<L, typename get1<typename L::Rows, typename L::Cols, typename L::order>::type> { };
};

//begin2 
template<typename Iterator2> struct begin2;

template<typename It1> 
struct begin2<sparse_iterator1<It1> > : 
    sparse_iterator2<
        typename meta::begin<
            typename meta::deref<It1>::type
        >::type
    > { };

template<typename Layout, typename Pos1>
struct begin2<dense_iterator1<Layout, Pos1> > : dense_iterator2<Layout, Pos1, meta::int_<0> > { }; 

//end2
template<typename Iterator2> struct end2;

template<typename It1> 
struct end2<sparse_iterator1<It1> > : 
    sparse_iterator2<
        typename meta::end<
            typename meta::deref<It1>::type
        >::type
    > { };

template<typename Layout, typename Pos1>
struct end2<dense_iterator1<Layout, Pos1> > : 
    dense_iterator2<
        Layout, 
        Pos1,
        typename get2<typename Layout::Rows, typename Layout::Cols, typename Layout::order>::type
    > { }; 


} //namespace layout


namespace meta {

//sparse_iterator1
template<typename It1>
struct next<layout::sparse_iterator1<It1> > : layout::sparse_iterator1<typename next<It1>::type> { };

template<typename It1>
struct prior<layout::sparse_iterator1<It1> > : layout::sparse_iterator1<typename prior<It1>::type> { };

template<typename It1>
struct deref<layout::sparse_iterator1<It1> > : deref<typename deref<It1>::type> { };

//sparse_iterator2
template<typename It2>
struct next<layout::sparse_iterator2<It2> > : layout::sparse_iterator2<typename next<It2>::type> { };

template<typename It2>
struct prior<layout::sparse_iterator2<It2> > : layout::sparse_iterator2<typename prior<It2>::type> { };

template<typename It2>
struct deref<layout::sparse_iterator2<It2> > : deref<It2> { };

//dense_iterator1
template<typename Layout, typename Pos1>
struct next<layout::dense_iterator1<Layout, Pos1> > : layout::dense_iterator1<Layout, typename next<Pos1>::type> { }; 

template<typename Layout, typename Pos1>
struct prior<layout::dense_iterator1<Layout, Pos1> > : layout::dense_iterator1<Layout, typename prior<Pos1>::type> { }; 

template<typename Layout, typename Pos1>
struct deref<layout::dense_iterator1<Layout, Pos1> > : 
    layout::entry<
        typename layout::get1<Pos1, int_<0>, typename Layout::order>::type,
        typename layout::get2<Pos1, int_<0>, typename Layout::order>::type,
        layout::_v_
    > { };

//dense_iterator2
template<typename Layout, typename Pos1, typename Pos2>
struct next<layout::dense_iterator2<Layout, Pos1, Pos2> > : layout::dense_iterator2<Layout, Pos1, typename next<Pos2>::type> { }; 

template<typename Layout, typename Pos1, typename Pos2>
struct prior<layout::dense_iterator2<Layout, Pos1, Pos2> > : layout::dense_iterator2<Layout, Pos1, typename prior<Pos2>::type> { }; 

template<typename Layout, typename Pos1, typename Pos2>
struct deref<layout::dense_iterator2<Layout, Pos1, Pos2> > : 
    layout::entry<
        typename layout::get1<Pos1, Pos2, typename Layout::order>::type,
        typename layout::get2<Pos1, Pos2, typename Layout::order>::type,
        layout::_v_
   > { };

//advance
template<typename It1, typename N>
struct advance<layout::sparse_iterator1<It1>, N> : 
    layout::sparse_iterator1<typename advance<It1, N>::type> { };

template<typename It2, typename N>
struct advance<layout::sparse_iterator2<It2>, N> : 
    layout::sparse_iterator2<typename advance<It2, N>::type> { };

template<typename Layout, typename Pos1, typename N>
struct advance<layout::dense_iterator1<Layout, Pos1>, N> : 
    layout::dense_iterator1<Layout, typename plus<Pos1, N>::type> { };

template<typename Layout, typename Pos1, typename Pos2, typename N>
struct advance<layout::dense_iterator2<Layout, Pos1, Pos2>, N> : 
    layout::dense_iterator2<Layout, Pos1, typename plus<Pos2, N>::type> { };

//distance
template<typename It1, typename It2>
struct distance<layout::sparse_iterator1<It1>, layout::sparse_iterator1<It2> > : distance<It1, It2>  { };

template<typename It1, typename It2>
struct distance<layout::sparse_iterator2<It1>, layout::sparse_iterator2<It2> > : distance<It1, It2>  { };

template<typename Layout1, typename Pos1, typename Layout2, typename Pos2>
struct distance<layout::dense_iterator1<Layout1, Pos1>, layout::dense_iterator1<Layout2, Pos2> > 
    : minus<Pos2, Pos1> {}; 

template<typename Layout1, typename Pos1_1, typename Pos1_2, typename Layout2, typename Pos2_1, typename Pos2_2>
struct distance<layout::dense_iterator2<Layout1, Pos1_1, Pos1_2>, layout::dense_iterator2<Layout2, Pos2_1, Pos2_2> > 
    : minus<Pos2_2, Pos1_2> {}; 

//equal_to
template<typename It1, typename It2>
struct equal_to<layout::sparse_iterator1<It1>, layout::sparse_iterator1<It2> > : equal_to<It1, It2> {};

template<typename It1, typename It2>
struct equal_to<layout::sparse_iterator2<It1>, layout::sparse_iterator2<It2> > : equal_to<It1, It2> {};

template<typename Layout1, typename Pos1, typename Layout2, typename Pos2>
struct equal_to<layout::dense_iterator1<Layout1, Pos1>, layout::dense_iterator1<Layout2, Pos2> > 
    : equal_to<Pos1, Pos2> {}; 

template<typename Layout1, typename Pos1_1, typename Pos1_2, typename Layout2, typename Pos2_1, typename Pos2_2>
struct equal_to<layout::dense_iterator2<Layout1, Pos1_1, Pos1_2>, layout::dense_iterator2<Layout2, Pos2_1, Pos2_2> > 
    : equal_to<Pos1_2, Pos2_2> {}; 

//not_equal_to
template<typename It1, typename It2>
struct not_equal_to<layout::sparse_iterator1<It1>, layout::sparse_iterator1<It2> > : not_equal_to<It1, It2> {};

template<typename It1, typename It2>
struct not_equal_to<layout::sparse_iterator2<It1>, layout::sparse_iterator2<It2> > : not_equal_to<It1, It2> {};

template<typename Layout1, typename Pos1, typename Layout2, typename Pos2>
struct not_equal_to<layout::dense_iterator1<Layout1, Pos1>, layout::dense_iterator1<Layout2, Pos2> > 
    : not_equal_to<Pos1, Pos2> {}; 

template<typename Layout1, typename Pos1_1, typename Pos1_2, typename Layout2, typename Pos2_1, typename Pos2_2>
struct not_equal_to<layout::dense_iterator2<Layout1, Pos1_1, Pos1_2>, layout::dense_iterator2<Layout2, Pos2_1, Pos2_2> > 
    : not_equal_to<Pos1_2, Pos2_2> {}; 

} //namespace meta

} //namespace mvl


#endif

